﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Globalization;
using System.Web;
using System.Reflection;
using System.Security.Cryptography;
using System.Runtime.Serialization;
using System.IO;


namespace AppPressFramework
{

    [DataContract]
    public class FieldValue
    {
        /// <summary>
        /// Set the label to be diplayed for the field in UI
        /// </summary>
        public string FieldLabel;
        /// <summary>
        /// Tag to be used when field is hidded. ???
        /// </summary>
        public string HiddenTag = null;
        internal bool DoNotSaveInDB = false;
        /// <summary>
        /// Value of the Field
        /// </summary>
        [DataMember]
        public string Value = null;
        /// <summary>
        /// ???
        /// </summary>
        [DataMember]
        public long fieldDefId;
        /// <summary>
        /// Readonly property of the field
        /// </summary>
        [DataMember]
        public FieldReadonlyType ReadOnly;
        [DataMember]
        internal string security;
        /// <summary>
        /// Hide the field. 
        /// Fields are hidden by removing the HTML of the field. On Browser no html will be seen for hidden fields
        /// If all cells of a column are hidden, the whole column is hidden.
        /// </summary>
        [DataMember]
        public FieldHiddenType Hidden;

        /// <summary>
        /// ControlStyle Property of the field
        /// </summary>
        public string ControlStyle = null;
        /// <summary>
        /// LabelStyle Property of the field
        /// </summary>
        public string LabelStyle = null;
        /// <summary>
        /// title property of the field
        /// </summary>
        public string Title = null;
        /// <summary>
        /// internal use only
        /// </summary>
        public FormData FormData; // points to formData containing this fieldValue
        internal FormField formField; // points to the formField representing the fieldValue
        internal bool ReArranged = false;
        internal bool Modified = false;
        internal string MetaData = null;// for select contains comma seperated values in select input
        internal bool NotFromClient;
        public FieldValue()
        { }
        public FieldValue(FieldValue fieldValue)
        {
            Type t = typeof(FieldValue);
            foreach (FieldInfo fieldInf in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                fieldInf.SetValue(this, fieldInf.GetValue(fieldValue));
            }
            var idx = FormData.fieldValues.FindIndex(t1 => t1.fieldDefId == fieldValue.fieldDefId);
            if (idx == -1)
                throw new Exception(fieldValue.GetFieldDescription() + " Internal Error: Could not Clone");
            FormData.fieldValues.RemoveAt(idx);
            FormData.fieldValues.Insert(idx, this);
            var a = FormData.a;
            //if (a.fieldValue == fieldValue)
            //{
            //    a.fieldValue = this;
            //}
            //foreach (var f in a.formDatas)
            //    if (f.containerFieldValue == fieldValue)
            //        f.containerFieldValue = this;

        }
        /// <summary>
        /// Error property of the field. 
        /// </summary>
        public string Error
        {
            set
            {
                if (FormData.a.appPressResponse.Find(t => t.appPressResponseType == AppPressResponseType.FieldError && t.fieldDefId == formField.id) == null)
                    FormData.a.appPressResponse.Add(AppPressResponse.FieldError(this, value));
            }
            get
            {
                var ar = FormData.a.appPressResponse.Find(t => t.appPressResponseType == AppPressResponseType.FieldError && t.fieldDefId == formField.id);
                if (ar != null)
                    return ar.message;
                return null;
            }
        }
        public FormDefFieldType GetFieldType()
        {
            return formField.Type;
        }
        public string GetFieldName()
        {
            return formField.fieldName;
        }
        /// <summary>
        /// Set Focus to the field
        /// </summary>
        public void SetFocus()
        {
            FormData.a.appPressResponse.Add(AppPressResponse.SetFocus(FormData.formDefId, FormData.id, formField));
        }

        /// <summary>
        /// ToString method has been overwridden to give error. Force user to use .val
        /// </summary>
        /// <returns></returns>
        //public override string ToString()
        //{
        //    return "You are not allowed to convert AppPress Field to string. To get value of a Field use val property of the field";
        //}
        internal void SetFieldValue(string value)
        {
            FormData.a.appPressResponse.Add(AppPressResponse.SetFieldValue(FormData.formDefId, FormData.id, formField, value));
        }
        /// <summary>
        /// Do a Required Validation on the Field
        /// </summary>
        /// <param name="message">Message to show if the validation fails</param>
        public void RequiredValidation(string message = null)
        {
            if (Value == null)
            {
                if (message.IsNullOrEmpty())
                    message = AppPress.GetLocalizationKeyValue("LAKey_RequiredMsg");
                var a = FormData.a;
                a.appPressResponse.Add(AppPressResponse.FieldError(this, message));
                throw new AppPressException();
            }
        }
        internal string GetFormattedFieldValue(AppPress a, DateTime dateTime)
        {
            if (Value.IsNullOrEmpty())
                return "";
            return dateTime.ToString("d", new CultureInfo("en-gb"));
        }
        /// <summary>
        /// Get formatted field value for the field
        /// </summary>
        /// <param name="a"></param>
        /// <param name="htmlFormat">if true encodes html characters like < to &lt; for correct display as html</param>
        /// <returns></returns>
        public string GetFormattedFieldValue(AppPress a, bool htmlFormat = true, string DateFormat = null)
        {
            if (Value == null && formField.Static && !formField.StaticSubmitValue && !(a.CallReason == CallReasonType.PageLoad || a.CallReason == CallReasonType.Refresh))
                throw new Exception("Form: " + formField.formDef.formName + " Field: " + formField.fieldName + " is of type Static. To use its value outside of Init, Add attribute &lt;Static SubmitValue=\"true\"/&gt to the Field in XML");
            switch (formField.Type)
            {
                case FormDefFieldType.DateTime:
                    {
                        var dateTimeFieldValue = (DateTimeFieldValue)this;
                        if (Value.IsNullOrEmpty())
                            return "";
                        if (formField.Style == FormDefFieldStyle.Time && dateTimeFieldValue.BaseDateTime != null)
                        {
                            //if (dateTimeFieldValue.BaseDateTime == null)
                            //    throw new Exception("Form: " + formField.formDef.formName + " Field: " + formField.formDef.formName + ". Cannot have BaseDateTime null for DateTime field of Style: Time");
                            var dateTime = DateTime.Parse(Value);
                            TimeSpan t = dateTime - dateTimeFieldValue.BaseDateTime.Value.Date;
                            // if day if not same as BaseDateTime return +1 or -1
                            var s = "";
                            var dayDiff = (dateTime.DayOfYear + dateTime.Year * 366) - (dateTimeFieldValue.BaseDateTime.Value.DayOfYear + dateTimeFieldValue.BaseDateTime.Value.Year * 366);
                            if (dayDiff != 0)
                                s += (Math.Sign(dayDiff) < 0 ? "-" : "+") + Math.Abs(dayDiff) + " ";
                            s += t.Hours.ToString("D2");
                            if (formField.TimeFormat == TimeFormat.Minutes || formField.TimeFormat == TimeFormat.Seconds)
                                s += ":" + t.Minutes.ToString("D2");
                            if (formField.TimeFormat == TimeFormat.Seconds)
                                s += ":" + t.Seconds.ToString("D2");
                            return s;
                            //return t.ToString("HH:mm");
                        }

                        var dateFormat = DateFormat;
                        if (dateFormat.IsNullOrEmpty())
                        {
                            dateFormat = AppPress.Settings.NetDateFormat;
                            if (a.remoteLoginUserId != null)
                                dateFormat = a.remoteData.NetDateFormat;

                            if (formField.Style == FormDefFieldStyle.DateTime)
                            {
                                dateFormat = AppPress.Settings.NetDateTimeFormat;
                                if (a.remoteLoginUserId != null)
                                    dateFormat = a.remoteData.NetDateTimeFormat;
                                if (dateFormat == null)
                                    dateFormat = AppPress.Settings.NetDateFormat + " HH:mm";
                            }
                        }
                        
                        return DateTime.Parse(Value).ToString(dateFormat, System.Globalization.CultureInfo.InvariantCulture);

                    }
                case FormDefFieldType.Pickone:
                    {
                        if (Value == null || Value.Trim().Length == 0)
                            return "";
                        var s = ((PickFieldValue)this).GetOption(a, Value).value;
                        if (a.skinType == SkinType.FO)
                            s = s.Replace("&", "&amp;").Replace("<", "&lt;");
                        return s;
                    }
                case FormDefFieldType.PickMultiple:
                    {
                        if (Value == null || Value.Trim().Length == 0)
                            return "";
                        var s = "";
                        var vs = Value.Split(new char[] { ',' });
                        foreach (var s1 in vs)
                        {
                            var s2 = ((PickFieldValue)this).GetOption(a, s1).value;
                            if (a.skinType == SkinType.FO)
                                s2 = s2.Replace("&", "&amp;").Replace("<", "&lt;");
                            if (!s.IsNullOrEmpty())
                                s += " | ";
                            s += s2;
                        }
                        return s;
                    }
                case FormDefFieldType.Checkbox:
                    {
                        if (formField.Static || a.skinType == SkinType.FO)
                            // ??? Get it from table like Pickone
                            return Value == "1" ? "Yes" : "No";
                        return Value == "1" ? "checked" : "";
                    }
                case FormDefFieldType.Number:
                    {
                        return FormDef.FormatNumber(formField, Value);
                    }
                case FormDefFieldType.Password:
                    return Value;
                case FormDefFieldType.TextArea:
                case FormDefFieldType.Text:
                case FormDefFieldType.HTML:
                    {
                        var tempValue = Value;
                        // to break long words in PDF
                        if (tempValue != null && a.skinType == SkinType.FO && formField.Type != FormDefFieldType.HTML)
                        {
                            if (tempValue.IndexOf(' ') == -1)
                            {
                                var ca = Value.ToCharArray();
                                var sb = new StringBuilder();
                                for (int i = 0; i < ca.Length; ++i)
                                {
                                    sb.Append(ca[i]);
                                    sb.Append('\u200B');
                                }
                                tempValue = sb.ToString();
                            }
                        }
                        if (tempValue == null)
                            tempValue = "";
                        else
                            if (a.skinType == SkinType.FO)
                                tempValue = tempValue.Replace("&", "&amp;").Replace("<", "&lt;");
                            else if (htmlFormat)
                                tempValue = HttpUtility.HtmlAttributeEncode(tempValue);
                        if (!tempValue.IsNullOrEmpty() && tempValue.StartsWith("LKey_"))
                            tempValue = AppPress.GetLocalizationKeyValue(tempValue);
                        return tempValue;
                    }


            }
            return Value;
        }
        /// <summary>
        /// Refresh the field in UI
        /// </summary>
        public void Refresh(AppPress a)
        {
            if (a.CallReason != CallReasonType.PageLoad)
                a.appPressResponse.Add(AppPressResponse.RefreshField(FormData.a, this, true));
        }
        /// <summary>
        /// Validate the field.
        /// </summary>
        public void Validate()
        {
            if (!_Validate())
                throw new AppPressException();
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public bool GetFieldBool()
        {
            if (FormData != null)
                FormData.a.AddDependentField(this);
            return Value != "0";
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public void SetFieldBool(bool value)
        {
            if (FormData != null)
                FormData.a.AddReverseDependentField(this);
            Value = value ? "1" : "0";
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public int? GetFieldInt()
        {
            if (FormData != null)
                FormData.a.AddDependentField(this);
            if (Value == null)
                return null;
            return int.Parse(Value);
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public void SetFieldInt(int? value)
        {
            if (FormData != null)
                FormData.a.AddReverseDependentField(this);
            Value = value == null ? null : value.ToString();
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public decimal? GetFieldDecimal()
        {
            if (FormData != null)
                FormData.a.AddDependentField(this);
            if (Value == null)
                return null;
            return decimal.Parse(Value);
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public void SetFieldDecimal(decimal? value)
        {
            if (FormData != null)
                FormData.a.AddReverseDependentField(this);
            Value = value == null ? null : value.ToString();
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public DateTime? GetFieldDateTime()
        {
            if (FormData != null)
                FormData.a.AddDependentField(this);
            if (Value == null)
                return null;
            return DateTime.Parse(Value);
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public void SetFieldDateTime(DateTime? value)
        {
            if (FormData != null)
                FormData.a.AddReverseDependentField(this);
            if (value == null)
                Value = null;
            else
                Value = value.Value.ToString(DAOBasic.DBDateTimeFormat);
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public string GetFieldString()
        {
            if (FormData != null)
                FormData.a.AddDependentField(this);
            return Value;
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public void SetFieldString(string value)
        {
            if (FormData != null)
                FormData.a.AddReverseDependentField(this);
            Value = value;
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public List<int> GetFieldPickMultiple()
        {
            if (FormData != null)
                FormData.a.AddDependentField(this);
            return FormData.GetFieldPickMultiple(formField.fieldName);
        }
        /// <summary>
        /// Internal use only
        /// </summary>
        /// <returns></returns>
        public void SetFieldPickMultiple(List<int> value)
        {
            if (FormData != null)
                FormData.a.AddReverseDependentField(this);
            FormData.SetFieldPickMultiple(formField.fieldName, value);
        }

        internal FormContainerFieldValue GetFormContainer()
        {
            var containerFieldName = formField.containerFormField.fieldName;
            return (FormContainerFieldValue)FormData.GetFieldValue(containerFieldName);
        }
        internal string _GetHtmlId()
        {
            return FormData.formDefId + AppPress.IdSep + formField.id + AppPress.IdSep + FormData.id;
        }
        /// <summary>
        /// ???
        /// </summary>
        /// <returns></returns>
        public string GetHtmlId()
        {
            return "AppPress" + AppPress.IdSep + (int)formField.Type + AppPress.IdSep + FormData.formDefId + AppPress.IdSep + formField.id + AppPress.IdSep + FormData.id;
        }
        /// <summary>
        /// ???
        /// </summary>
        /// <returns></returns>
        public string GetHtmlColumnId()
        {
            return "AppPress" + AppPress.IdSep + (int)formField.Type + AppPress.IdSep + FormData.formDefId + AppPress.IdSep + formField.id;
        }

        internal string GetHtmlErrorId()
        {
            return "error" + AppPress.IdSep + FormData.formDef.id + AppPress.IdSep + formField.id + AppPress.IdSep + FormData.id;
        }
        internal string GetHtmlContainerId()
        {
            return "fieldContainer" + AppPress.IdSep + FormData.formDef.id + AppPress.IdSep + formField.id + AppPress.IdSep + FormData.id;
        }

        internal string GetSkin(AppPress a, out bool outer)
        {
            // Test cases in Empire
            // NewEmployee, AppraisalMaster->ManageEmployee->Add, DivisionManagement->Add SubDivisionHead, EmployeeProfile->FinancialDetails
            // go up till we find Form containing this field as part of skin
            var formDef = FormData.formDef;
            var upFieldValues = new List<FieldValue>();
            var containerFieldValue = FormData.containerFieldValue;
            while (containerFieldValue != null && FormData.callerFieldValue == null)
            {
                if (containerFieldValue.formField.rowFormDef == null)
                    break;
                upFieldValues.Add(containerFieldValue);
                formDef = containerFieldValue.FormData.formDef;
                containerFieldValue = containerFieldValue.FormData.containerFieldValue;
                if (containerFieldValue == null)
                    break;
            }
            var skin = formDef.GetSkin(a, false, false, null, SkinType.HTML);
            string fieldSkin;
            int startIndex;
            // go down till we get skin for formField.formDef
            for (int i = upFieldValues.Count() - 1; i >= 0; --i)
            {
                upFieldValues[i].formField.RemoveBetweenMarkers(a, skin, 0, "", out startIndex, out fieldSkin, out outer);
                if (fieldSkin != null) // sometimes do not get upField Markers
                    skin = fieldSkin;
            }
            //if (FormData.containerFieldValue != null && (FormData.containerFieldValue.formField.Type == FormDefFieldType.FormContainerGrid || FormData.containerFieldValue.formField.Type == FormDefFieldType.FormContainerDynamic))
            if (FormData.containerFieldValue != null && FormData.containerFieldValue.formField.rowFormDef != null)
            {
                FormData.containerFieldValue.formField.RemoveBetweenMarkers(a, skin, 0, "Row", out startIndex, out fieldSkin, out outer);
                skin = fieldSkin;
            }
            formDef = FormData.formDef;
            if (formField.containerFormField != null)
            {
                // For FormContainer Fields get the skin out containing field and loook in that
                var cFieldValue = FormData.GetFieldValue(formField.containerFormField.id);
                bool outer1;
                skin = cFieldValue.GetSkin(a, out outer1);
            }

            // remove markers for all other fields first as field with same name may be nested in other fields
            foreach (var f in formDef.formFields)
                if (f.id != formField.id && (f.containerFormField == null || f.containerFormField.id != formField.id))
                {
                    //StartIndex = 1 to ignore top level marker name same as f.fieldName. Correction was done for SubDivionHead refresh in DivisionManagement.
                    skin = f.RemoveBetweenMarkers(a, skin, 1, "", out startIndex, out fieldSkin, out outer);
                }
            formField.RemoveBetweenMarkers(a, skin, 0, "", out startIndex, out skin, out outer);
            return skin;
        }
        internal string GetSecurityKey(AppPress a)
        {
            if (FormData == null || formField.Type == FormDefFieldType.FormContainerDynamic || formField.Static)
                return null;

            SHA1 sha = new SHA1CryptoServiceProvider();
            // This is one implementation of the abstract class SHA1.

            string s = "";
            if (formField.Type == FormDefFieldType.Pickone)
            {
                if (Value != null && Value.Trim().Length > 0) //Ram: Blank ID should be treated as null
                {
                    var optionsFunction = formField.FieldFunctions.Find(t => t.ServerFunctionType == FunctionType.Options);
                    if (optionsFunction != null)
                    {
                        var option = ((PickFieldValue)this).GetOption(a, Value);
                        if (option == null)
                            // option should be valid
                            throw new Exception("Security Error");
                    }
                }
            }
            s += _GetHtmlId();

            if (ReadOnly != FieldReadonlyType.None)
                s += GetFormattedFieldValue(a, true, "dd/MM/yyyy").Trim();
            if (Hidden != FieldHiddenType.None)
                s += "NonH";
            if (AppPress.Settings.developer)
                return s;

            var ba = sha.ComputeHash(Encoding.ASCII.GetBytes(s));
            return string.Concat(ba.Select(b => b.ToString("X2")).ToArray());
        }

        internal string GetHtmlOnClick(AppPress a)
        {
            switch (formField.Type)
            {
                case FormDefFieldType.Checkbox:
                    return GetHtmlOnChange(a);
                case FormDefFieldType.Button:
                case FormDefFieldType.Pickone:
                case FormDefFieldType.FileUpload:
                    if (formField.FieldFunctions == null)
                        throw new Exception("Could not find OnClick function in Field: " + formField.fieldName + " in Form: " + formField.formDef.formName);
                    string s = string.Empty;
                    if (formField.Type == FormDefFieldType.Checkbox)
                        if (!formField.DoNotSaveInDB)
                            s += "SetDirty();";
                    s += GetOnClickJSScript();
                    return s;
                default:
                    throw new Exception("Cannot Get AppPressOnClick for " + GetFieldDescription() + " which is of Type: " + formField.Type);
            }
        }

        internal bool InvokeOn(AppPress a, string functionName, bool invoke)
        {
            var fieldTypes = new List<Type>();
            var className = formField.GetClassName();
            Type fieldType1 = Util.GetType(className);
            if (fieldType1 != null)
                fieldTypes.Add(fieldType1);
            className = formField.GetClassName(FormData.formDef);
            fieldType1 = Util.GetType(className);
            if (fieldType1 != null)
                if (fieldTypes.Find(t => t == fieldType1) == null)
                    fieldTypes.Add(fieldType1);
            bool methodInvoked = false;
            foreach (var fieldType in fieldTypes)
            {
                for (int i = Util.Assemblies.Count() - 1; i >= 0; --i)
                {
                    var assembly = Util.Assemblies[i];
                    var method = assembly.appLogicType.GetMethod(functionName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(AppPress), fieldType }, null);
                    if (method != null)
                        if (invoke)
                            try
                            {
                                a.serverFunction = new ServerFunction();
                                Object o = a.fieldValue;
                                if (o.GetType() != fieldType)
                                {
                                    o = Activator.CreateInstance(fieldType, new object[] { this });
                                }
                                Util.InvokeMethod(a, method, new object[] { a, o });
                                methodInvoked = true;
                            }
                            finally
                            {
                                a.serverFunction = null;
                            }
                        else
                            return true;
                }
            }
            return methodInvoked;
        }


        internal bool _Validate()
        {
            var a = FormData.a;
            var pFieldValue = a.fieldValue;
            var pFormData = a.fieldValue.FormData;
            var result = true;
            try
            {
                a.fieldValue = this;
                a.fieldValue.FormData = FormData;
                int clientActionCount = a.appPressResponse.Count;
                if (formField.Required)
                    AppPressLogic.RequiredValidation(a);
                if (formField.EmailValidation)
                    AppPressLogic.EmailValidation(a);
                if (formField.RegexValidation != null)
                    AppPressLogic.RegexValidation(a, formField.RegexValidation);
                if (formField.MinimumValue != null || formField.MaximumValue != null)
                    AppPressLogic.RangeValidation(a);
                if (a.appPressResponse.Count > clientActionCount)
                    result = false;
            }
            finally
            {
                a.fieldValue = pFieldValue;
                a.fieldValue.FormData = pFormData;
            }
            // intrinsic validation with fieldType
            switch (formField.Type)
            {
                case FormDefFieldType.FormContainerDynamic:
                    {
                        foreach (var fd in a.formDatas)
                            if (fd.containerFieldValue == this)
                                foreach (var f in fd.fieldValues)
                                    if (!f._Validate())
                                        result = false;
                        break;
                    }
                case FormDefFieldType.Number:
                    decimal d;
                    if (!String.IsNullOrEmpty(Value) && !decimal.TryParse(Value, out d))
                    {
                        result = false;
                        a.appPressResponse.Add(AppPressResponse.FieldError(this, "This Number is not valid"));
                    }
                    break;
                case FormDefFieldType.DateTime:
                    if (Value != null)
                    {
                        var dateTimeFieldValue = (DateTimeFieldValue)this;
                        if (dateTimeFieldValue.MinDate != null && DateTime.Parse(Value) < dateTimeFieldValue.MinDate)
                            a.appPressResponse.Add(AppPressResponse.FieldError(this, "Date should be more or equal to " + dateTimeFieldValue.MinDate.Value.ToString(AppPress.Settings.NetDateFormat)));
                        if (dateTimeFieldValue.MaxDate != null && DateTime.Parse(Value) > dateTimeFieldValue.MaxDate)
                            a.appPressResponse.Add(AppPressResponse.FieldError(this, "Date should be less than " + dateTimeFieldValue.MaxDate.Value.ToString(AppPress.Settings.NetDateFormat)));
                    }
                    break;
            }
            return result;
        }

        internal string ReplaceAppPress(AppPress a, string fieldSkin)
        {
            var htmlId = GetHtmlId();
            fieldSkin = fieldSkin.Replace("AppPressId", htmlId).
                Replace("AppPressErrorId", GetHtmlErrorId()).
                Replace("AppPressContainerId", GetHtmlContainerId()).
                Replace("AppPressColumnId", GetHtmlColumnId());
            if (fieldSkin.IndexOf("AppPressOnClick") != -1)
                fieldSkin = fieldSkin.Replace("AppPressOnClick", GetHtmlOnClick(a));
            if (fieldSkin.IndexOf("AppPressOnChange") != -1)
                fieldSkin = fieldSkin.Replace("AppPressOnChange", GetHtmlOnChange(a));
            if (fieldSkin.IndexOf("AppPressDisplayName") != -1)
                fieldSkin = fieldSkin.Replace("AppPressDisplayName", GetDisplayName(a));
            if (fieldSkin.IndexOf("AppPressFieldName") != -1)
                fieldSkin = fieldSkin.Replace("AppPressFieldName", formField.fieldName);

            if (fieldSkin.IndexOf("AppPressMaxLength") != -1)
            {
                var maxLength = "5000000";
                if (this.formField.Type == FormDefFieldType.Number)
                    maxLength = (16 + this.formField.decimals).ToString();
                else if (this.formField.MaxChars != null)
                    maxLength = this.formField.MaxChars.ToString();
                fieldSkin = fieldSkin.Replace("AppPressMaxLength", maxLength);
            }

            

            var controlStyle = "";
            if (this.ControlStyle != null)
                controlStyle = " style='" + this.ControlStyle + "' ";
            else if (this.formField.ControlStyle != null)
                controlStyle = " style='" + this.formField.ControlStyle + "' ";
            fieldSkin = fieldSkin.Replace("AppPressControlStyle", controlStyle);

            var labelStyle = "";
            if (this.LabelStyle != null)
                labelStyle = " style=\"" + this.LabelStyle + "\" ";
            else if (this.formField.LabelStyle != null)
                labelStyle = " style=\"" + this.formField.LabelStyle + "\" ";
            fieldSkin = fieldSkin.Replace("AppPressLabelStyle", labelStyle);

            if (this.formField.Type == FormDefFieldType.FileUpload)
            {
                int i; string outStr;
                if (this.ReadOnly != FieldReadonlyType.None)
                {
                    fieldSkin = FormField.RemoveBetweenMarkers(a, fieldSkin, 0, "<!--|AppPress.FileUpload.DeletePartBegin|-->", "<!--|AppPress.FileUpload.DeletePartEnd|-->", out i, out outStr, null);
                    fieldSkin = FormField.RemoveBetweenMarkers(a, fieldSkin, 0, "<!--|AppPress.FileUpload.UploadPartBegin|-->", "<!--|AppPress.FileUpload.UploadPartEnd|-->", out i, out outStr, null);
                }
                if (this.Value == null)
                {
                    fieldSkin = FormField.RemoveBetweenMarkers(a, fieldSkin, 0, "<!--|AppPress.FileUpload.FileNamePartBegin|-->", "<!--|AppPress.FileUpload.FileNamePartEnd|-->", out i, out outStr, null);
                    fieldSkin = FormField.RemoveBetweenMarkers(a, fieldSkin, 0, "<!--|AppPress.FileUpload.DeletePartBegin|-->", "<!--|AppPress.FileUpload.DeletePartEnd|-->", out i, out outStr, null);
                }
                else
                {
                    if (fieldSkin.IndexOf("AppPressFileUploadDownloadUrl") != -1)
                        if (this.formField.DoNotSaveInDB)
                            fieldSkin = fieldSkin.Replace("AppPressFileUploadDownloadUrl", a.GetFileUrl(this.Value));
                        else
                            fieldSkin = fieldSkin.Replace("AppPressFileUploadDownloadUrl", a.GetFileUrl(long.Parse(this.Value)));
                    if (fieldSkin.IndexOf("AppPressFileUploadFileName") != -1)
                    {
                        if (this.formField.DoNotSaveInDB)
                            fieldSkin = fieldSkin.Replace("AppPressFileUploadFileName", Path.GetFileName(this.Value));
                        else if (this.Value != null)
                        {
                            var fileDetail = AppPress.GetFile(int.Parse(this.Value));
                            fieldSkin = fieldSkin.Replace("AppPressFileUploadFileName", fileDetail == null ? "" : fileDetail.FileName);
                        }
                    }
                    if (fieldSkin.IndexOf("AppPressFileUploadDeleteUrl") != -1)
                        fieldSkin = fieldSkin.Replace("AppPressFileUploadDeleteUrl", "DeleteFile('" + htmlId + "'," + AppPress.LocalInstanceId + ")");
                }
            }

            return fieldSkin;
        }

        internal string CompileSkin(AppPress a, string skin)
        {
            var fieldValue = this;
            var formField = fieldValue.formField;
            if (formField.Type != FormDefFieldType.FormContainerDynamic && formField.Type != FormDefFieldType.ForeignKey)
            {
                // remove all formContainer skins
                var formContainers = new List<string>();
                var formContainerKeys = new List<string>();
                if (formField.formDef != null) // in some unknown case this code is causing problem after some use of Site
                    foreach (var f in fieldValue.FormData.formDef.formFields)
                        if (f != null && f.Type == FormDefFieldType.FormContainerDynamic)
                        {
                            string fieldUnique, fieldSkin, beginMarker, endMarker;
                            bool outer;
                            skin = f.ExtractBetweenMarkers(a, skin, 0, "", out fieldUnique, out fieldSkin, out outer, out beginMarker, out endMarker);
                            if (fieldSkin != null)
                                if (!outer)
                                    skin = skin.Replace(fieldUnique, beginMarker + fieldSkin + endMarker);
                                else
                                {
                                    formContainers.Add(beginMarker + fieldSkin + endMarker);
                                    formContainerKeys.Add(fieldUnique);
                                }

                        }


                bool found = false;
                bool appPressIdFound = formField.Static || formField.Type == FormDefFieldType.FileUpload || a.skinType != SkinType.HTML;
                while (true) // multiple begin End Markers for a Field. Eg Date Range
                {
                    string fieldSkin, fieldUnique, beginMarker, endMarker;
                    bool outer;
                    var formName = FormData.formDef.formName;
                    skin = formField.ExtractBetweenMarkers(a, skin, 0, "", out fieldUnique, out fieldSkin, out outer, formName, out beginMarker, out endMarker);
                    if (fieldSkin == null)
                    {
                        //if (!found && formField.Type != FormDefFieldType.Extension && formField.Type != FormDefFieldType.UserControl && formField.Type != FormDefFieldType.UserControlScalar && !formField.Hidden)
                        //    throw new Exception(formField.GetFieldDescription() + " Could not find Begin Marker " + HttpUtility.HtmlEncode(beginMarker) + "\n This can be due to following\n\tThe markers are missing.\n\tThe markers are not in correct position. \nYou can also check by looking at markers in AppPress generated skin from dev links. For more information look at Working with Skins ???");
                        if (!found)
                            appPressIdFound = true;
                        break;
                    }
                    if (fieldValue.Hidden == FieldHiddenType.Hidden)
                    {
                        //if (fieldValue.Hidden == FieldHiddenType.Hidden)
                        //{
                        //    var containerFieldValue = fieldValue.FormData.containerFieldValue;
                        //    while (containerFieldValue != null)
                        //        if (containerFieldValue.Hidden == FieldHiddenType.Hidden)
                        //            break;
                        //        else
                        //            containerFieldValue = containerFieldValue.FormData.containerFieldValue;
                        //    if (containerFieldValue != null)
                        //        return skin; // found a hidden container
                        //    bool outer;
                        //    // add a td container inside hidden field for use in fresh.
                        //    // if td not insode table it will work like span
                        //    var hiddenMessage = "<span id='" + fieldValue.GetHtmlContainerId() + "' ";
                        //    if (fieldValue.formField.OriginalType == FormDefFieldType.DateRange)
                        //        hiddenMessage += "style='display:table-cell'";
                        //    hiddenMessage += ">" + fieldValue.HiddenMessage + "</span>";
                        //    skin = fieldValue.formField.RemoveBetweenMarkers(a, skin, "", out outer, hiddenMessage);
                        //} 
                        var containerFieldValue = fieldValue.FormData.containerFieldValue;
                        while (containerFieldValue != null)
                            if (containerFieldValue.Hidden == FieldHiddenType.Hidden)
                                break;
                            else
                                containerFieldValue = containerFieldValue.FormData.containerFieldValue;
                        if (containerFieldValue != null)
                            continue; // found a hidden container
                        if (fieldValue.HiddenTag != null && fieldValue.HiddenTag.Length == 0)
                            skin = skin.Replace(fieldUnique, "");
                        else
                        {
                            var tag = fieldValue.HiddenTag;
                            if (tag == null && fieldValue.FormData.containerFieldValue != null && fieldValue.FormData.containerFieldValue.formField.OriginalType == FormDefFieldType.FormContainerGrid && !FormData.formDef.Pivot)
                                tag = "td";
                            if (tag == null)
                                tag = "span";
                            var hiddenMessage = "<" + tag + " id='" + fieldValue.GetHtmlContainerId() + "' ";
                            if (fieldValue.formField.OriginalType == FormDefFieldType.DateRange)
                                hiddenMessage += "style='display:table-cell'";
                            hiddenMessage += "></" + tag + ">";
                            skin = skin.Replace(fieldUnique, hiddenMessage);
                        }
                        continue;
                    }
                    found = true;
                    if (!outer || appPressIdFound || fieldSkin.IndexOf("AppPressId") != -1)
                        appPressIdFound = true;
                    string partUnique;
                    fieldSkin = fieldValue.ReplaceAppPress(a, fieldSkin);
                    var htmlId = fieldValue.GetHtmlId();
                    if (!formField.Static)
                        if (formField.Type == FormDefFieldType.PickMultiple ||
                            (formField.Type == FormDefFieldType.Pickone &&
                                (formField.Style == FormDefFieldStyle.Radio || formField.Style == FormDefFieldStyle.DropDown)))
                        {
                            string partSkin;
                            fieldSkin = FormField.ExtractBetweenMarkers(a, fieldSkin, 0, "<!--|AppPress.PartBegin|-->", "<!--|AppPress.PartEnd|-->", out partUnique, out partSkin, formField.formDef.formName);
                            if (partSkin != null)
                            //throw new Exception(HttpUtility.HtmlEncode(formField.GetFieldDescription() + " In Skin Could not find <!--|AppPress.PartBegin|-->  <!--|AppPress.PartEnd|--> to generate options"));
                            {
                                string[] values = fieldValue.Value == null ? null : fieldValue.Value.Split(new char[] { ',' });
                                ((PickFieldValue)fieldValue).GetOptions(a);
                                a.fieldsGenerated.Add(fieldValue);
                                if (formField.Static)
                                {
                                    if (values != null)
                                        foreach (var value in values)
                                        {
                                            var partCompiled = partSkin.Replace("AppPressValue", ((PickFieldValue)fieldValue).options.Find(t => t.id == value).value, StringComparison.OrdinalIgnoreCase);
                                            fieldSkin = fieldSkin.Replace(partUnique, partCompiled + partUnique);
                                        }
                                }
                                else
                                {
                                    foreach (var option in ((PickFieldValue)fieldValue).options)
                                    {
                                        var appPressValue = option.id;
                                        if (formField.Type == FormDefFieldType.PickMultiple && values != null)
                                            foreach (var value in values)
                                                if (value == appPressValue)
                                                {
                                                    appPressValue += @""" checked=""checked";
                                                    break;
                                                }
                                        var partCompiled = partSkin.
                                            Replace("AppPressValue", appPressValue, StringComparison.OrdinalIgnoreCase).
                                            Replace("AppPressLabel", option.value);
                                        fieldSkin = fieldSkin.Replace(partUnique, partCompiled + partUnique);
                                    }

                                }
                                fieldSkin = fieldSkin.Replace(partUnique, "");
                            }
                            if (formField.Type == FormDefFieldType.PickMultiple)
                            {
                                a.JsStr.Append(@"_AppPress_UpdatePickMultipleOptions(""" + htmlId + @""");");
                                a.JsStr.Append(@"AppPress_AddPickMultipleToPopupDialogs(""" + htmlId + @""");");
                            }
                        }
                    fieldSkin = fieldSkin.Replace("AppPressValue", GetFieldValue(a));
                    skin = skin.Replace(fieldUnique, fieldSkin);
                    if (!formField.Static)
                    {

                        if (formField.Type == FormDefFieldType.DateTime)
                            a.JsStr.Append(a.GetHtmlDatePopup(formField.fieldName));

                        if (formField.Type == FormDefFieldType.Checkbox)
                            if (Value == "1")
                                a.JsStr.Append("$('#'+JQueryEscape('" + GetHtmlId() + "')).attr('checked',true);\n");

                        if (formField.Type == FormDefFieldType.FileUpload)
                            a.JsStr.Append(a.GetFileUploadScript(formField.fieldName));

                        if (formField.Type == FormDefFieldType.Number)
                        {
                            var regexString = "/^\\d+$/";
                            if (formField.decimals > 0)
                                regexString = "/^\\d+(?:\\.\\d+)?$/";
                            a.JsStr.Append(@"
                                $(JQueryEscape('#" + htmlId + @"')).blur(
                                    function(){
                                        RegexValidate($(JQueryEscape('#" + htmlId + "'))," + regexString + ",$(JQueryEscape('#" + GetHtmlErrorId() + @"')),'This Number is not valid')
                                    });");
                        }
                        if (formField.Type == FormDefFieldType.DateTime)
                        {
                            var dateFormat = AppPress.Settings.JQueryDateFormat;
                            if (a.remoteLoginUserId != null)
                                dateFormat = a.remoteData.JQueryDateFormat;
                            a.JsStr.Append(@"
                                $(JQueryEscape('#" + htmlId + @"')).change(
                                    function(){
                                        dateValidate($(JQueryEscape('#" + htmlId + "')),'" + dateFormat + "',$(JQueryEscape('#" + GetHtmlErrorId() + @"')),'This date is not valid')
                                    });");
                        }
                        if (formField.Type == FormDefFieldType.Text)
                            if (formField.EmailValidation)
                                a.JsStr.Append(@"
                                $(JQueryEscape('#" + htmlId + @"')).blur(
                                    function(){
                                        RegexValidate($(JQueryEscape('#" + htmlId + "')),/^\\s*([\\w-]+(?:\\.[\\w-]+)*)@((?:[\\w-]+\\.)*\\w[\\w-]{0,66})\\.([a-z]{2,6}(?:\\.[a-z]{2})?)\\s*$/,$(JQueryEscape('#" + GetHtmlErrorId() + @"')),'This is not a valid email')
                                    });");
                            else if (formField.RegexValidation != null)
                                a.JsStr.Append(@"
                                $(JQueryEscape('#" + htmlId + @"')).blur(
                                    function(){
                                        RegexValidate($(JQueryEscape('#" + htmlId + "')),new RegExp('" + formField.RegexValidation.Replace("'", "\\'") + "'),$(JQueryEscape('#" + GetHtmlErrorId() + @"')),'This field contains invalid characters')
                                    });");
                        if (formField.Type == FormDefFieldType.Pickone)
                            if (formField.Style == FormDefFieldStyle.DropDown)
                                a.JsStr.Append("$(JQueryEscape('#" + htmlId + "')).val('" + fieldValue.Value + "');");
                            else if (formField.Style == FormDefFieldStyle.Radio)
                            {
                                a.JsStr.Append("$(JQueryEscape('#" + htmlId + AppPress.IdSep + fieldValue.Value + "')).prop('checked','checked');");
                                if (fieldValue.ReadOnly == FieldReadonlyType.Readonly)
                                    a.JsStr.Append("$('[id^=" + htmlId.Replace(AppPress.IdSep, "\\\\" + AppPress.IdSep) + "]').attr('disabled',true);");
                            }
                            else if (formField.Style == FormDefFieldStyle.AutoComplete)
                                a.JsStr.Append("AppPressAutoComplete('" + htmlId + "','" + a.GetHtmlOnChange(formField.fieldName) + "', " + AppPress.LocalInstanceId + ");");
                    }
                    if (fieldValue.Title != null)
                        a.JsStr.Append("$(JQueryEscape('#fieldContainer" + AppPress.IdSep + fieldValue._GetHtmlId() + "')).prop('title','" + fieldValue.Title + "');");
                }
                //if (!appPressIdFound)
                //    throw new Exception("In Form: " + formData.formDef.formName + " Field: " + formField.fieldName + " is defined as Static but the Skin does not use AppPressId");

                // insert back formContainers skins
                for (int i = 0; i < formContainers.Count(); ++i)
                    skin = skin.Replace(formContainerKeys[i], formContainers[i]);
            }

            return skin;

        }

        public FormDefFieldStyle Style { get { return formField.Style; } }

        internal string GetFieldDescription()
        {
            // for Error Display
            var s = formField.GetDescription();
            if (FormData != null)
                s+= ", FormDataId: " + FormData.id;
            return s;
        }

        internal string GetHtmlOnChange(AppPress a)
        {
            if (formField.Static && !formField.NonStatic)
                throw new Exception(formField.GetDescription() + " Cannot get OnChange for Static Field");
            switch (formField.Type)
            {
                case FormDefFieldType.Pickone:
                case FormDefFieldType.Text:
                case FormDefFieldType.TextArea:
                case FormDefFieldType.DateTime:
                case FormDefFieldType.Number:
                case FormDefFieldType.Checkbox:
                case FormDefFieldType.Password:
                    {
                        if (formField.Type == FormDefFieldType.Checkbox && formField.fieldName == "SelectRow")
                        {
                            if (formField.AllowMultiSelect)
                                return "";
                            if (FormData.containerFieldValue != null)
                            {
                                var modifyField = FormData.containerFieldValue.FormData.fieldValues.Find(t => t.formField.fieldName == "Modify" + FormData.containerFieldValue.formField.fieldName);
                                if (modifyField != null && modifyField.Hidden != FieldHiddenType.None)
                                    return "";
                            }
                            return "UnSelectRestSelectRow(this)";
                        }
                        bool always = formField.FieldFunctions.Find(t => t.ServerFunctionType == FunctionType.OnChange) != null;
                        if (!always)
                        {
                            always = InvokeOn(a, "OnChange", false);
                        }
                        var str = "OnChange(this," + AppPress.LocalInstanceId + "," + (always ? "true" : "false") + ");";
                        if (!formField.DoNotSaveInDB)
                            str += "SetDirty();";


                        var needAjaxCall = formField.FieldFunctions.Find(t => t.ServerFunctionType == FunctionType.OnChange) != null;
                        if (!needAjaxCall)
                            needAjaxCall = InvokeOn(a, "OnChange", false);
                        if (needAjaxCall)
                        {
                            if (formField.Shortcut != null && a.JsStr != null)
                                a.JsStr.Append("shortcut.add('" + formField.Shortcut + "',function (){HandleFocusShortcut($(\"#\"+JQueryEscape('" + GetHtmlId() + "')))})\n");
                        }



                        return str;
                    }
                default:
                    throw new Exception("Cannot AppPressOnChange for field which is of Type: " + formField.Type);
            }

        }
        /// <summary>
        /// ???
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string GetSetValueJSScript(string value)
        {
            return " SetFieldValue(" + FormData.formDefId + @", " + FormData.id + @", " + formField.id + @", " + value + @"); ";
        }
        /// <summary>
        /// Returns script to perform a click on the field
        /// If you want to perform a click on HTML element or from visualization graph, crete a hidden button and add this function to onclick tag of element
        /// </summary>
        /// <returns></returns>
        public string GetOnClickJSScript()
        {
            var s = "";
            var a = FormData.a;
            if (formField.Type != FormDefFieldType.Button)
                throw new Exception(GetFieldDescription() + " GetOnClickJSScript can be called only for type Button.");
            var ajaxStr = "AjaxFunctionCall(\"OnClick\",\"" + AppPress.LocalInstanceId + "\",\"" + FormData.id + "\",\"" + fieldDefId + "\"," + (formField.NoSubmit ? "true" : "false") + ",this);";
            if (formField.Shortcut != null && a.JsStr != null)
                a.JsStr.Append("shortcut.add('" + formField.Shortcut + "',function (){HandleShortcut($(\"#\"+JQueryEscape('" + GetHtmlId() + "')))})\n");
            s += ajaxStr;
            return s;
        }

        internal string GetDisplayName(AppPress a)
        {
            if (formField.fieldName == "SelectRow")
                return "";
            if (formField.Type == FormDefFieldType.Button && Value != null)
                return Value;
            var suffix = "";
            if (formField != null && formField.Required)
                suffix = "<span style='color:Red'>*</span>";
            if (FieldLabel != null) // fieldValue1 is coming null for Column Headers
                return FieldLabel + suffix;
            if (formField.displayFieldName != null)
                return formField.displayFieldName + suffix;
            var tempfieldName = AppPress.GetLocalizationKeyValue(formField.fieldName, false);
            if (tempfieldName != null)
                return tempfieldName + suffix;
            var fieldName = AppPress.InsertSpacesBetweenCaps(formField.fieldName);
            return fieldName + suffix;
        }

        internal string GetFieldValue(AppPress a)
        {
            if (a.fieldsGenerated.Find(t => t == this) == null)
                a.fieldsGenerated.Add(this);
            var pFieldValue = a.fieldValue;
            try
            {
                a.fieldValue = this;
                return GetFormattedFieldValue(a, formField.Type != FormDefFieldType.HTML);
            }
            finally
            {
                a.fieldValue = pFieldValue;
            }
        }

        public void ErrorException(string error)
        {
            Error = error;
            throw new AppPressException();
        }
        /// <summary>
        /// ??? internal use only
        /// </summary>
        /// <returns></returns>
        public long? GetDBId()
        {
            return formField.dbId == 0 ? (long?)null : formField.dbId;
        }
    }
    [DataContract]
    public class DateTimeFieldValue : FieldValue
    {
        [DataMember]
        public DateTime? BaseDateTime = null;
        [DataMember]
        public DateTime? MinDate = null;
        [DataMember]
        public DateTime? MaxDate = null;
        public DateTimeFieldValue() { }
        public DateTimeFieldValue(DateTimeFieldValue fieldValue)
            : base(fieldValue)
        {
            this.BaseDateTime = fieldValue.BaseDateTime;
            this.MinDate = fieldValue.MinDate;
            this.MaxDate = fieldValue.MaxDate;
        }
    }
    [DataContract]
    public class FileUploadFieldValue : FieldValue
    {
        public FileUploadFieldValue() { }
        public FileUploadFieldValue(FileUploadFieldValue fieldValue)
            : base(fieldValue)
        {
        }
    }
    [DataContract]
    public class NumberFieldValue : FieldValue
    {
        public NumberFieldValue() { }
        public NumberFieldValue(NumberFieldValue fieldValue) : base(fieldValue) { }
        public decimal? Number
        {
            get
            {
                return Value == null ? (decimal?)null : decimal.Parse(Value);
            }
            set
            {
                if (value == null)
                    Value = null;
                else
                    Value = value.ToString();
            }
        }
    }
    [DataContract]
    public class PickFieldValue : FieldValue
    {
        internal List<Option> options = null;
        public string autoCompleteTerm;
        public PickFieldValue() { }
        public PickFieldValue(PickFieldValue fieldValue)
            : base(fieldValue)
        {
            this.autoCompleteTerm = fieldValue.autoCompleteTerm;
            this.options = fieldValue.options;
        }
        internal List<Option> GetOptions(AppPress a, string id)
        {
            var pFieldValue = a.fieldValue;
            var pServerFunction = a.serverFunction;
            options = null;
            try
            {
                a.fieldValue = this;
                var optionsFunction = formField.FieldFunctions.Find(t => t.ServerFunctionType == FunctionType.Options);
                Type t1 = null;
                switch (formField.formDef.FormType)
                {
                    case FormType.UserControlScalarForm:
                        t1 = Util.GetType(a, formField.formDef, formField.formDef.formFields[0].id); // will have only one field
                        break;
                    case FormType.MergedForm:
                        t1 = Util.GetType(a, formField.formDef, formField.id);
                        break;
                    default:
                        t1 = Util.GetType(a, FormData.formDef, formField.id);
                        break;
                }
                if (t1 == null)
                    throw new Exception(formField.GetDescription() + " Could not find Type for the field in namespace ApplicationClasses. Check if TT has been built.");
                a.AddToDependentFields = true;
                a.sourceField = this;

                var method = Util.GetMethod(a, "Options", new Type[] { typeof(AppPress), t1 });
                if (method != null)
                {
                    if (formField.Type == FormDefFieldType.Pickone)
                    {
                        var pickoneFieldValue = (PickFieldValue)this;
                        pickoneFieldValue.autoCompleteTerm = a.autoCompleteTerm;
                    }
                    Object o = this;
                    if (o.GetType() != t1)
                    {
                        o = Activator.CreateInstance(t1, new object[] { this });
                        a.sourceField = (FieldValue)o;
                    }
                    var obj = Util.InvokeMethod(a, method, new object[] { a, o });
                    if (obj == null)
                        return new List<Option>();
                    if (obj.GetType() == typeof(List<Option>))
                        return (List<Option>)obj;
                    if (obj.GetType() != typeof(string))
                        throw new Exception("Options for " + t1.ToString() + " should return a class");
                    options = AppPressLogic.GetOptionsFromQuery(a, this, (string)obj);
                    if (optionsFunction != null)
                        throw new Exception("Cannot have Calc and Options Function defined for Field: " + formField.fieldName + " in Form: " + formField.formDef.formName);
                }
                string tableName = null;
                if (options == null)
                {
                    tableName = FormData.formDef.TableName;
                    if (optionsFunction == null && tableName != null)
                    {
                        if (formField.options != null)
                        {

                            return CloneOptions();
                        }
                        string query;
                        if (DAOBasic.databaseType == DatabaseType.MySql)
                            query = @"
                            SELECT REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME 
                            FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
                            WHERE TABLE_NAME = '" + tableName + @"'
                                AND COLUMN_NAME = '" + formField.fieldName + @"'
                                AND REFERENCED_TABLE_NAME IS NOT NULL 
                                AND " + DAOBasic.SchemaColumnName + @" = '" + a.site.dbName + "'";
                        else
                            query = @"
                                SELECT 
                                    tab2.name AS [referenced_table_name],
                                    col2.name AS [referenced_column_name],
                                    obj.name AS FK_NAME,
                                    sch.name AS [schema_name],
                                    tab1.name AS [table_name],
                                    col1.name AS [column_Name]
                                FROM sys.foreign_key_columns fkc
                                INNER JOIN sys.objects obj
                                    ON obj.object_id = fkc.constraint_object_id
                                INNER JOIN sys.tables tab1
                                    ON tab1.object_id = fkc.parent_object_id
                                INNER JOIN sys.schemas sch
                                    ON tab1.schema_id = sch.schema_id
                                INNER JOIN sys.columns col1
                                    ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
                                INNER JOIN sys.tables tab2
                                    ON tab2.object_id = fkc.referenced_object_id
                                INNER JOIN sys.columns col2
                                    ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
                                WHERE tab1.name = '" + tableName + @"'
                                AND col1.name = '" + formField.fieldName + @"'";
                        var dr = a.ExecuteQuery(query);
                        try
                        {
                            if (dr.Read())
                            {
                                var rTableName = dr.GetString(0);
                                var rColumnName = dr.GetString(1);
                                var displayColumnName = formField.fieldName;
                                dr.Close();
                                var exists = a.ExecuteString(@"
                                    SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
                                    WHERE TABLE_NAME = '" + rTableName + @"' and COLUMN_NAME = '" + displayColumnName + "' and " + DAOBasic.SchemaColumnName + @"='" + a.site.dbName + "'") != null;
                                if (!exists)
                                {
                                    // get second column
                                    displayColumnName = a.ExecuteString(@"
                                        SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
                                        WHERE TABLE_NAME = '" + rTableName + @"' And DATA_TYPE in ('varchar','nvarchar') and " + DAOBasic.SchemaColumnName + @"='" + a.site.dbName + "' Order By ORDINAL_POSITION");
                                    if (displayColumnName == null)
                                        throw new Exception("Could not Find DisplayColumn Name for Pick Field:" + a.fieldValue.formField.fieldName);
                                }
                                if (formField.Style != FormDefFieldStyle.AutoComplete)
                                {
                                    dr = a.ExecuteQuery("Select " + DAOBasic.SQLQuote + rColumnName + DAOBasic.SQLQuote + ", " + DAOBasic.SQLQuote + displayColumnName + DAOBasic.SQLQuote + " From " + DAOBasic.SQLQuote + rTableName + DAOBasic.SQLQuote + " Order by " + DAOBasic.SQLQuote + rColumnName + DAOBasic.SQLQuote);
                                    formField.options = new List<Option>();
                                    while (dr.Read())
                                    {
                                        var s = dr.IsDBNull(1) ? "" : dr[1].ToString();
                                        formField.options.Add(new Option { id = dr[0].ToString(), value = s });
                                    }
                                    if (a.fieldValue.formField.Style == FormDefFieldStyle.DropDown)
                                    {
                                        if (!a.fieldValue.formField.Required)
                                            formField.options.Insert(0, new Option { id = null, value = "" });
                                    }
                                    return CloneOptions();
                                }
                                else
                                {
                                    optionsFunction = new ServerFunction(a, FunctionType.Options, "GenericGetOptionsFromTable");
                                    optionsFunction.Parameters.Add(new ServerFunctionParameter { Name = "TableName", Value = rTableName });
                                    optionsFunction.Parameters.Add(new ServerFunctionParameter { Name = "IdColumnName", Value = rColumnName });
                                    optionsFunction.Parameters.Add(new ServerFunctionParameter { Name = "DisplayColumnName", Value = displayColumnName });
                                }
                            }
                        }
                        finally { dr.Close(); }
                    }
                    if (optionsFunction != null)
                    {
                        a.serverFunction = optionsFunction;
                        if (a.functionCall == "AutoCompleteOptions")
                            ((PickFieldValue)a.fieldValue).autoCompleteTerm = a.autoCompleteTerm;
                        options = (List<Option>)Util.InvokeMethod(a, optionsFunction.method, new object[] { a, id });
                        if (a.fieldValue.formField.Style == FormDefFieldStyle.DropDown)
                        {
                            if (a.fieldValue.Value == null || !a.fieldValue.formField.Required)
                                options.Insert(0, new Option { id = null, value = "" });
                        }
                    }
                    else
                    {
                        var message = "";
                        if (tableName != null)
                            message += "Cannot Find ForeignKey for column:" + formField.fieldName + " in Table: " + tableName + " to generate options for Pickone<br/>";
                        message += "Could not find Options function" + FormData.formDef._GenerateCode(0, formField, "Options");
                        this.Error = "<a onclick=\"AlertMessage('" + HttpUtility.HtmlAttributeEncode(message.Replace("'", "\\'").Replace("\"", "\\\"").Replace(Environment.NewLine, "<br/>")) + "',850)\"<span style='color:red'>X</span></a>";
                        options = new List<Option>();
                    }
                }

            }
            finally
            {
                a.AddToDependentFields = false;
                a.serverFunction = pServerFunction;
                a.fieldValue = pFieldValue;
            }

            return options;

        }

        private List<Option> CloneOptions()
        {
            var opts = new List<Option>();
            foreach (var option in formField.options)
            {
                var opt = new Option { id = option.id, value = option.value };
                if (!opt.value.IsNullOrEmpty() && opt.value.StartsWith("LKey"))
                {
                    opt.value = AppPress.GetLocalizationKeyValue(opt.value);
                }
                opts.Add(opt);
            }
            if (Value != null && formField.Required)
                opts.RemoveAll(t => t.id == null); // for required field if options is selected then no need for blnk option
            return opts;
        }

        /// <summary>
        /// Get the Option Associated with the Pick field
        /// </summary>
        /// <param name="a"></param>
        /// <param name="id">Id of the option</param>
        /// <returns></returns>
        public Option GetOption(AppPress a, string id)
        {
            Option option = null;
            if (options != null)
                option = options.Find(t => t.id == id);
            if (option == null)
            {
                var ioptions = GetOptions(a, id);
                option = ioptions.Find(t => t.id == id);
            }
            if (option == null)
                if (Error != null)
                    return new Option { id = id, value = "" };
                else
                    throw new Exception(GetFieldDescription() + " Could not find option with id:" + id);
            return option;
        }


        public List<Option> GetOptions(AppPress a)
        {
            //Commented for ParentFieldName in PickOne.
            //if (options != null)
            //    return options;
            options = GetOptions(a, null);
            return options;
        }
    }
    [DataContract]
    public class ButtonFieldValue : FieldValue
    {
        public ButtonFieldValue() { }
        public ButtonFieldValue(ButtonFieldValue fieldValue)
            : base(fieldValue)
        {
        }
        [DataMember]
        internal bool HiddenForDialogButton = false;

    }
    [DataContract]
    public class FormContainerFieldValue : FieldValue
    {
        public FormContainerFieldValue() { }
        internal int rowNumber = 0;
        public List<FormData> GetFieldFormContainer()
        {
            //formData.a.AddDependentField(this);
            return FormData.GetFieldFormContainer(formField.fieldName);
        }
        public void SetFieldFormContainer(List<FormData> value)
        {
            //formData.a.AddReverseDependentField(this);
            SetContainedFormDatas(value);
        }

        public void AddNewForm(AppPress a)
        {
            AppPressLogic.AddNewForm(a, (long?)null, this);
        }
        public void AddNewForm(AppPress a, FormData formData)
        {
            AppPressLogic.AddNewForm(a, formData, this);
        }
        public FormData TryGetSingleSelection()
        {
            var selectedFormDatas = AppPressLogic.GetSelectedFormDatas(FormData.a, this);
            if (selectedFormDatas.Count() != 1)
                return null;
            return selectedFormDatas.First();
        }
        public FormData GetSingleSelection()
        {
            var selectedFormDatas = AppPressLogic.GetSelectedFormDatas(FormData.a, this);
            if (selectedFormDatas.Count() != 1)
            {
                throw new AppPressException("Please select any one row by clicking check-box on the left column in required row to execute this step.");
            }
            return selectedFormDatas.First();
        }
        public List<FormData> GetSelection()
        {
            return AppPressLogic.GetSelectedFormDatas(FormData.a, this);
        }
        public void DeleteSelectedSubForms()
        {
            var a = FormData.a;
            a.PromptClient("Do you really want to delete selected records.");
            AppPressLogic.DeleteSelectedSubForms(a, null, true);
        }
        public void SetContainedFormDatas(IEnumerable<FormData> newFormDatas)
        {
            var a = FormData.a;
            foreach (var newFormData in newFormDatas)
            {
                newFormData.containerFieldValue = this;
                newFormData.pageStackIndex = FormData.pageStackIndex;
                var containerIdFieldValue = newFormData.fieldValues.Find(t => t.formField.Type == FormDefFieldType.ForeignKey);
                if (containerIdFieldValue != null && containerIdFieldValue.Value == null)
                    containerIdFieldValue.Value = FormData.id;
                newFormData.a = a;
                newFormData.pageStackIndex = FormData.pageStackIndex;
                if (newFormData.id != null)
                    foreach (var f in newFormDatas)
                        if (f != newFormData && f.formDef == newFormData.formDef && f.id == newFormData.id)
                            throw new Exception("In Form Container Grid: " + formField.fieldName + " Cannot have 2 members with same FormName: " + f.formDef.formName + " and same id: " + f.id + " ");
            }
            var pLen = a.formDatas.Count();
            foreach (var newFormData in newFormDatas)
            {
                var foundIndex = a.formDatas.FindIndex(t1 => t1.formDefId == newFormData.formDefId && t1.id == newFormData.id && t1.containerFieldValue == newFormData.containerFieldValue);
                if (foundIndex != -1 && foundIndex < pLen)
                {
                    if (a.formDatas[foundIndex] != newFormData)
                    {
                        if (a.formDatas[foundIndex].IsDeleted)
                            a.formDatas[foundIndex].IsDeleted = false;
                        if (a.formDatas[foundIndex].IsSubmitted)
                        {
                            // add missing fields
                            // if Static Field with RetailValue is changed from Code, the value should be set from DB
                            a.formDatas[foundIndex].MergeFields(newFormData);
                            a.formDatas[foundIndex].IsSubmitted = false;
                        }
                        // keep the sorting order same as newFormDatas
                        var pFormData = a.formDatas[foundIndex];
                        a.formDatas.RemoveAt(foundIndex);
                        a.formDatas.Add(pFormData);
                    }
                }
                else
                    a.formDatas.Add(newFormData);
            }
            var dfds = new List<FormData>();

            var deletedFormDatas = a.formDatas.FindAll(t => t.containerFieldValue == this && !t.IsDeleted && !t.fromPopupSave && newFormDatas.FirstOrDefault(t1 => t1.formDefId == t.formDefId && t1.id == t.id) == null);
            foreach (var deletedFormData in deletedFormDatas)
                Util.ApplyOnChildFormDatas(a.formDatas, deletedFormData, t => dfds.Add(t));
            a.formDatas.RemoveAll(t => dfds.Find(t1 => t1 == t) != null);
            a.CheckContainerfieldValues();
        }
        internal List<FormData> GetContainedFormDatas(AppPress a)
        {
            return a.formDatas.FindAll(t => t.containerFieldValue == this);
        }
        /// <summary>
        /// read FormDatas for the FormContainer using the uery
        /// </summary>
        /// <param name="a"></param>
        /// <param name="query"></param>
        /// <returns></returns>
        public List<FormData> ReadFormDatas(AppPress a, string query)
        {
            return FormData.ReadFormDatas(a, this, formField.GetContainerRowFormDef(a), query);
        }
        public List<FormData> GetMasterContainer(AppPress a)
        {
            //This will be handled through iFrame in master skin.
            if (a.Request["AppPress.ContentUrl"] != null)
                return null;
            if (a.MasterContentAreaFormName == null)
                throw new Exception("In GetMasterContainer, Request must have formName");

            string id = null;
            var s = a.Request["s"];
            if (s != null)
            {
                s = Util.DecryptDES(s);
                var s1 = s.Split(new string[] { AppPress.IdSep }, StringSplitOptions.None);
                id = s1[1];
            }
            else
                id = a.PageURL["id"];
            if (id == null)
                id = AppPress.GetUniqueId().ToString();

            if (id == "l")
                id = a.sessionData.loginUserId;
            var formDef = AppPress.FindFormDef(a.MasterContentAreaFormName);
            if (formDef.FormType == FormType.MasterForm)
                throw new Exception("Cannot Redirect to Master Form:" + a.MasterContentAreaFormName);
            var formDatas = new List<FormData>();

            var formData = FormData.InitializeFormData(a, formDef, id);
            formData.containerFieldValue = a.fieldValue;
            formDatas.Add(formData);
            return formDatas;
        }

        /// <summary>
        /// Use in FormContainerDynamic Control to dynamically bind a FormType to FormContiner control. Some use cases are
        /// - Embed a form inside another form
        /// - Embed a form depending on another pickone field
        /// If the field had another type of form bound to it deletes them
        /// If FormName is bound to a table returns forms bound to parent form with foreign Key field in formName
        /// If no form is bound returns a new form of type formName
        /// </summary>
        /// <param name="a"></param>
        /// <param name="formDefId">Id of the form. If this is null makes the formContainer empty</param>
        /// <returns></returns>
        public List<FormData> BindSingleForm(AppPress a, long? formDefId)
        {
            var formDatas = new List<FormData>();
            FormDef subFormDef = null;
            if (formDefId != null)
            {
                subFormDef = AppPress.FindFormDef(formDefId.Value);
                if (subFormDef == null)
                    throw new Exception(a.fieldValue.GetFieldDescription() + ". Function BindSingleForm. Could not find Form with FormDefId: " + formDefId);
                if (formField.OriginalType != FormDefFieldType.EmbeddedForm)
                {
                    var containerIdFormField = subFormDef.formFields.Find(t => t.Type == FormDefFieldType.ForeignKey);
                    if (subFormDef.TableName != null && containerIdFormField == null && a.fieldValue.FormData.formDef.TableName != null)
                        throw new Exception("Could not Find field of Type ForeignKey in Form:" + subFormDef.formName);
                }
                var subFormDefId = subFormDef.id;
                if (formDatas.Count == 0)
                    formDatas = AppPressLogic.GetContainerRowForms(a, subFormDefId);
                if (formDatas.Count == 0)
                {
                    var formData = FormData.NewFormData(a, subFormDef, a.fieldValue);
                    formDatas.Add(formData);
                }
            }
            foreach (var formData in a.formDatas.FindAll(t => t.containerFieldValue == a.fieldValue && (subFormDef == null || t.formDef.id != subFormDef.id)))
                Util.ApplyOnChildFormDatas(a.formDatas, formData, t => t.IsDeleted = true);

            return formDatas;
        }


        /// <summary>
        /// Clear Error for the Field
        /// </summary>
        /// <param name="fieldValue"></param>
        public void ClearErrors(AppPress a)
        {
            var index = a.appPressResponse.FindIndex(t => t.appPressResponseType == AppPressResponseType.ClearErrors);
            a.appPressResponse.Insert(index + 1, AppPressResponse.ClearErrors(this));
        }
    }
}
